package com.hushan.elevator.controller;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.bean.BeanUtil;

import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.hushan.elevator.dto.UserAddDto;
import com.hushan.elevator.model.Department;
import com.hushan.elevator.model.Role;
import com.hushan.elevator.model.User;
import com.hushan.elevator.security.SecurityUtils;
import com.hushan.elevator.service.DeptService;
import com.hushan.elevator.service.RoleService;
import com.hushan.elevator.service.UserService;
import com.hushan.elevator.utils.Result;
import com.hushan.elevator.utils.ResultCode;
import com.hushan.elevator.utils.exception.UserException;
import com.hushan.elevator.utils.exception.UserLoginException;
import com.hushan.elevator.vo.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
@RequestMapping("/user")
@RestController
@Tag(name = "userController", description = "用户功能接口文档")
public class UserController {
    private LineCaptcha lineCaptcha;
    @Value("${spring.security.session-key}")
    String SESSION_KEY;  // captcha session key

    private final UserService userService;
    private final DeptService deptService;
    private final RoleService roleService;

    public UserController(UserService userService, DeptService deptService, RoleService roleService) {
        this.userService = userService;
        this.deptService = deptService;
        this.roleService = roleService;
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Result login(@RequestBody LoginVo form, HttpServletRequest request) {
        String username = form.getUsername();
        String password = form.getPassword();
        String code = form.getVerifyCode();
        System.out.println(lineCaptcha.getCode());
        // code does not exist
        // or
        // code error
        if (code == null || !code.equalsIgnoreCase(lineCaptcha.getCode())) {
            return new Result(ResultCode.FAIL, "验证码错误");
        }
        try {

            User user = userService.findUserByTelNumb(username);
            // is password correct
            userService.verifyUser(user, username, password);
            // get token
            String token = userService.authentication(username, password);
            // update latest ip, time
            user.setLatestIp(request.getRemoteAddr());
            user.setLatestTime(LocalDateTime.now());
            userService.updateUser(user);
            return new Result(ResultCode.SUCCESS, token);
        } catch (UserLoginException | UserException e) {
            return new Result(ResultCode.FAIL, e.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage());
            return new Result(ResultCode.SERVER_BUSY);
        }
    }

    @GetMapping("/captcha")
    public void generateCaptcha(HttpServletResponse response) throws Exception {
        // create a captcha
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(100, 40, 4, 40);
        this.lineCaptcha = captcha;
        response.setContentType("image/jpeg");
        response.setHeader("Pragma", "No-cache");
        // output to web stream
        captcha.write(response.getOutputStream());
    }

    @RequestMapping(path = "", method = RequestMethod.POST)
    public Result addUser(@RequestBody UserAddVo userAddVo) {
        boolean isValid = Validator.isMobile(userAddVo.getTelNumb());
        if (!isValid) {
            return new Result(ResultCode.FAIL, "手机号码不符合规范");
        }
        /**
         * detect authority***/
        User creator = SecurityUtils.getUser();
        // creator dept
        Department creatorDept = deptService.findDeptByUserId(creator.getUserId());
        // added user dept
        Department addedUserDept = deptService.findDeptById(userAddVo.getDeptId());
        try {
            // detect {{department parent child}} relationship
            boolean b = deptService.detectDeptAuth(addedUserDept.getAncestors(), creatorDept.getDeptId()) // son dept
                    ||
                    Objects.equals(creatorDept.getDeptId(), addedUserDept.getDeptId()); // same dept
            ;
            // has not authority
            if (!b) {
                throw new Exception("has not authority");
            }
            // detect {{role}} relationship
            if (roleService.findRoleById(userAddVo.getRoleId()).getRoleLevel()
                    <
                    roleService.findRoleByUser(creator.getUserId()).getRoleLevel()) {
                throw new Exception("role authority relationship error");
            }
            // add user
            UserAddDto newUserDto = new UserAddDto();
            BeanUtil.copyProperties(userAddVo, newUserDto);
            userService.addUser(newUserDto);
            // detect add success or not
            User addedUser = userService.findUserByTelNumb(userAddVo.getTelNumb());
            if (addedUser == null) {
                throw new Exception("error");
            }
            // connect role, dept, user
            userService.addUserDeptConnect(addedUser.getUserId(), userAddVo.getDeptId(),creator.getUserId());
            userService.addUserRoleConnect(addedUser.getUserId(), userAddVo.getRoleId(),creator.getUserId());
        } catch (UserException e) {
            return new Result(ResultCode.FAIL, e.getMessage());
        } catch (Exception e) {
            return new Result(ResultCode.SERVER_BUSY, e.getMessage());
        }
        return Result.SUCCESS();
    }

    @Operation(summary = "获取用户单位下的用户列表", description = "根据单位ID获取该单位下的所有用户")
    @RequestMapping(path = "", method = RequestMethod.GET)
    public Result getUser() {
        User requestUser = SecurityUtils.getUser();
        Department requestUserDept = deptService.findDeptByUserId(requestUser.getUserId());
        List<Department> childDept = deptService.findChildByDeptId(requestUserDept.getDeptId());
        childDept.add(requestUserDept);
        try {
            List<UserDataVo> rst = new ArrayList<>();
            for (Department item : childDept) {
                List<UserDataVo> userVoByDept = getUserVoByDept(item);
                rst.addAll(userVoByDept);
            }
            return new Result(ResultCode.SUCCESS, rst);
        } catch (Exception e) {
            return new Result(ResultCode.SERVER_BUSY);
        }
    }
    @Operation(summary = "更新本人用户信息", description = "更新用户信息")
    @RequestMapping(path = "", method = RequestMethod.PUT)
    public Result updateUser(@RequestBody User data) throws UserException {
        User user = SecurityUtils.getUser();
        user.setSex(data.getSex());
        user.setTelNumb(data.getTelNumb());
        user.setEmail(data.getEmail());
        user.setTitle(data.getTitle());
        userService.updateUser(user);
        return Result.SUCCESS();
    }

    @Operation(summary = "修改本人密码", description = "修改本人密码")
    @RequestMapping(path = "/pwd", method = RequestMethod.PUT)
    public Result updatePwd(@RequestBody UpdatePwdVo data) throws UserException {
        if (!Objects.equals(data.getNewPwd(), data.getCheckPwd()))
            return new Result(ResultCode.FAIL,"两次密码不相同");
        User user = SecurityUtils.getUser();

        try {
            userService.verifyUser(user, user.getUsername(), data.getPwd());
            userService.changePwd(user,data.getNewPwd());
        } catch (Exception e) {
            return new Result(ResultCode.FAIL,e.getMessage());
        }
        userService.updateUser(user);
        return Result.SUCCESS();
    }



    @Operation(summary = "更新用户信息", description = "更新用户信息")
    @RequestMapping(path = "/{id}", method = RequestMethod.PUT)
    public Result updateUserById(@PathVariable Integer id, @RequestBody UserAddVo updateData) throws UserException {
        // not same info
        if (!Objects.equals(id, updateData.getUserId())) {
            return Result.FAIL();
        }
        // creator dept
        User creator = SecurityUtils.getUser();
        Department creatorDept = deptService.findDeptByUserId(creator.getUserId());

        // added user dept
        User updatedUser = userService.findUserByUserId(id);
        Department updatedUserOriginDept = deptService.findDeptByUserId(updatedUser.getUserId());
        Department updatedUserNewDept = deptService.findDeptByUserId(updateData.getDeptId());
        Role updatedUserOriginRole = roleService.findRoleByUser(updatedUser.getUserId());

        try {
            // detect {{department parent child}} origin relationship
            boolean a = deptService.detectDeptAuth(updatedUserOriginDept.getAncestors(), creatorDept.getDeptId()) //
                    // son dept
                    ||
                    Objects.equals(creatorDept.getDeptId(), updatedUserOriginDept.getDeptId()); // same dept
            if (!a) {
                throw new Exception("has not origin dept authority");
            }
            // detect {{department parent child}} new relationship
            boolean b = deptService.detectDeptAuth(updatedUserNewDept.getAncestors(), creatorDept.getDeptId()) //
                    // son dept
                    ||
                    Objects.equals(creatorDept.getDeptId(), updatedUserNewDept.getDeptId()); // same dept
            // has not authority
            if (!b) {
                throw new Exception("has not new dept authority");
            }
            // detect {{role}} relationship
            if (roleService.findRoleByUser(updatedUser.getUserId()).getRoleLevel()
                    <
                    roleService.findRoleByUser(creator.getUserId()).getRoleLevel()) {
                throw new Exception("origin role authority relationship error");
            }
            if (updatedUserOriginRole.getRoleLevel()
                    <
                    roleService.findRoleByUser(creator.getUserId()).getRoleLevel()) {
                throw new Exception("new role authority relationship error");
            }

        } catch (UserException e) {
            return new Result(ResultCode.FAIL, e.getMessage());
        } catch (Exception e) {
            return new Result(ResultCode.SERVER_BUSY, e.getMessage());
        }
        userService.updateUser(updateData);
        userService.updateDept(
                updateData.getUserId(),
                updatedUserOriginDept.getDeptId(),
                updateData.getDeptId());
        userService.updateRole(
                updateData.getUserId(),
                updatedUserOriginRole.getRoleId(),
                updateData.getRoleId());
        return Result.SUCCESS();
    }

    @Operation(summary = "根据ID删除用户", description = "根据部门ID删除用户")
    @DeleteMapping("/{id}")
    public Result delUserById(@PathVariable Integer id) {
        // deleted user
        Department deletedUserDept = deptService.findDeptByUserId(id);
        if (!deptService.findUserPermissionForDept(SecurityUtils.getUser(), deletedUserDept)) {
            return new Result(ResultCode.UN_AUTHORISE, "没有权限");
        }
        userService.deleteUser(userService.findUserByUserId(id));
        return new Result(ResultCode.SUCCESS, "删除成功");
    }


    @Operation(summary = "获取本人用户信息", description = "根据token获取用户信息，")
    @GetMapping("/info")
    public Result getUserInfo() {
        User user = SecurityUtils.getUser();
        Department dept = deptService.findDeptByUserId(user.getUserId());
        return new Result(ResultCode.SUCCESS, user,dept);
    }

    @Operation(summary = "获取子单位下的用户", description = "根据单位获取子单位下的用户")
    @GetMapping("/dept/c/{id}")
    public Result getUsersByDept(@PathVariable Integer id) {
        Department dept = deptService.findDeptById(id);
        boolean permission = deptService.findUserPermissionForDept(SecurityUtils.getUser(), dept);
        if (!permission) {
            return Result.FAIL();
        }
        List<Department> childDept = deptService.findChildByDeptId(id);
        childDept.add(dept);
        try {
            List<UserDataVo> rst = new ArrayList<>();
            for (Department item : childDept) {
                List<UserDataVo> userVoByDept = getUserVoByDept(item);
                rst.addAll(userVoByDept);
            }
            return new Result(ResultCode.SUCCESS, rst);
        } catch (Exception e) {
            return new Result(ResultCode.SERVER_BUSY);
        }
    }

    @Operation(summary = "获取本单位的用户", description = "根据单位获取本单位下的用户")
    @GetMapping("/dept/{id}")
    public Result getUserByDept(@PathVariable Integer id) {
        Department dept = deptService.findDeptById(id);
        boolean permission = deptService.findUserPermissionForDept(SecurityUtils.getUser(), dept);
        if (!permission) {
            return Result.FAIL();
        }
        try {
            List<UserDataVo> userVoByDept = getUserVoByDept(dept);
            return new Result(ResultCode.SUCCESS, userVoByDept);
        } catch (Exception e) {
            return new Result(ResultCode.SERVER_BUSY);
        }
    }

    public List<UserDataVo> getUserVoByDept(Department dept) {
        List<UserDataVo> rst = new ArrayList<>();
        List<User> usersByDept = userService.findUsersByDept(dept.getDeptId());
        for (User user : usersByDept) {
            UserDataVo userDataVo = new UserDataVo();
            BeanUtil.copyProperties(user, userDataVo);
            userDataVo.setDeptId(deptService.findDeptByUserId(user.getUserId()).getDeptId());
            userDataVo.setRoleId(roleService.findRoleByUser(user.getUserId()).getRoleId());
            rst.add(userDataVo);
        }
        return rst;
    }
}
